package com.turing.post.transaction.impl;

import java.io.BufferedInputStream;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

import com.turing.post.transaction.util.ChkAccFile;
import com.turing.post.transaction.util.ChkAccFileBusiness;
import com.turing.post.transaction.util.ChkAccFileDetail;
import com.turing.post.tuxedo.util.TransXmlHelper;
import com.turing.post.tuxedo.util.TuxedoRespCode;
import com.turing.post.util.PCPSApplicationException;
import com.turing.post.util.PCPSUtil;

import cz.dhl.ftp.Ftp;
import cz.dhl.ftp.FtpConnect;
import cz.dhl.ftp.FtpFile;
import cz.dhl.ftp.FtpInputStream;

@Stateless(mappedName = "transaction.impl.ChkAccFileBusinessImpl")
@Remote
@Local
public class ChkAccFileBusinessImpl implements ChkAccFileBusiness {

	/**
	 * 删除远程ftp文件
	 * 
	 * @param ftpaddress
	 * @param filename
	 * @throws PCPSApplicationException
	 */
	private void delRemoteFile(String ftpaddress, String filename) {
		Ftp cl = new Ftp();
		// cl.getContext().setActiveSocketMode(true);
		// cl.getContext().setFileTransferMode('I');
		// cl.getContext().setListCommandMode(FtpSetting.NAME_LIST_LS_LA);
		try {
			cl.connect(FtpConnect.newConnect(ftpaddress));
			cl.rm(filename);
			cl.disconnect();
			cl = null;
		} catch (Exception ex) {
			PCPSUtil.Log("删除[" + ftpaddress + "][" + filename + "]失败!", ex);
		} finally {
			if (cl != null) {
				cl.disconnect();
				cl = null;
			}
		}
	}

	/**
	 * 通过ftp获取远程文件
	 * 
	 * @param ftpaddress
	 * @param filename
	 * @return
	 * @throws PCPSApplicationException
	 */
	@Override
	public byte[] getRemoteFile(String ftpaddress, String filename)
			throws PCPSApplicationException {
		BufferedInputStream bis = null;
		Ftp cl = new Ftp();
		List<byte[]> abuf = new ArrayList<byte[]>();
		int totalsz = 0;
		try {
			// cl.getContext().setActiveSocketMode(false);
			// cl.getContext().setFileTransferMode('I');
			// cl.getContext().setListCommandMode(FtpSetting.NAME_LIST_LS_LA);
			boolean bret = cl.connect(FtpConnect.newConnect(ftpaddress));
			PCPSUtil.Log(ftpaddress + " 连接返回 " + bret);
			/* source FtpFile remote file */
			FtpFile file = new FtpFile(filename, cl);
			/* open ftp input stream */
			bis = new BufferedInputStream(new FtpInputStream(file));

			while (true) {
				byte[] buf = new byte[4096];
				int sz = bis.read(buf);
				if (sz > 0) {
					if (sz == buf.length)
						abuf.add(buf);
					else {
						byte[] bbuf = new byte[sz];
						System.arraycopy(buf, 0, bbuf, 0, sz);
						abuf.add(bbuf);
					}
					totalsz += sz;
				} else
					break;
			}
			bis.close();
			bis = null;
			cl.disconnect();
			cl = null;
		} catch (Exception e) {
			PCPSUtil.Log("FTP CHKACC FAILED", e);
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"FTP CHKACC FAILED!");
		} finally {
			try {
				if (bis != null)
					bis.close();
			} catch (Exception e) {
			}

			try {
				if (cl != null)
					cl.disconnect();
			} catch (Exception e) {
			}
		}

		if (totalsz <= 0)
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"FTP CHKACC SIZE <= 0 FAILED!");

		byte[] content = new byte[totalsz];
		int pos = 0;

		for (byte[] al : abuf) {
			System.arraycopy(al, 0, content, pos, al.length);
			pos += al.length;
		}

		return (content);
	}

	/**
	 * 产生商户某日对帐文件名
	 * 
	 * @param merchantNo
	 * @param tranDate
	 * @return
	 */
	private String generateMerchantChkAccFileName(String merchantNo,
			Date tranDate) {
		String filename = String.format("%s_%s.xml", merchantNo, PCPSUtil
				.getPackedYMDString(tranDate));
		return filename;
	}

	/**
	 * 从指定地址处删除指定商户、日期的对帐文件
	 * 
	 * @param ftpaddress
	 * @param filename
	 */
	public void delRemoteFile(String ftpaddress, String merchantNo,
			Date tranDate) {
		String filename = generateMerchantChkAccFileName(merchantNo, tranDate);
		delRemoteFile(ftpaddress, filename);
	}

	@Override
	public ChkAccFile getChkAccFile(String address, String merchantNo,
			Date tranDate) throws PCPSApplicationException {
		// 取文件内容
		String filename = generateMerchantChkAccFileName(merchantNo, tranDate);
		byte[] content = getRemoteFile(address, filename);
		if (content == null || content.length < 1)
			throw new PCPSApplicationException(TuxedoRespCode.RC_ACCCHKFILE,
					"对帐文件" + filename + "不存在!");
		return getChkAccFileFromRaw(content, merchantNo, tranDate);
	}

	private BigInteger getValidateBigInteger(BigInteger num)
			throws PCPSApplicationException {
		if (BigInteger.ZERO.compareTo(num) > 0)
			throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER, "["
					+ num + "]数字非法!");
		return num;
	}

	/**
	 * 解析summary章节
	 */
	private void parseSummary(Element root, ChkAccFile caf) throws Exception {
		Element summary = root.getChild(ChkAccFile.XML_SUMMARY);
		if (summary == null)
			throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
					"Summary 章节不存在!");

		caf.setDebitCount(getValidateBigInteger(TransXmlHelper.getInteger(
				summary, ChkAccFile.XML_DEBITCOUNT)));
		caf.setDebitAmount(getValidateBigInteger(TransXmlHelper.getInteger(
				summary, ChkAccFile.XML_DEBITAMOUNT)));
		caf.setCreditCount(getValidateBigInteger(TransXmlHelper.getInteger(
				summary, ChkAccFile.XML_CREDITCOUNT)));
		caf.setCreditAmount(getValidateBigInteger(TransXmlHelper.getInteger(
				summary, ChkAccFile.XML_CREDITAMOUNT)));
	}

	/**
	 * 解析明细部分
	 * 
	 * @param root
	 * @param caf
	 * @throws Exception
	 */
	private void parseDetails(Element root, ChkAccFile caf) throws Exception {
		Element details = root.getChild(ChkAccFile.XML_DETAILS);
		ArrayList<ChkAccFileDetail> fileDetail = new ArrayList<ChkAccFileDetail>();
		if (details == null)
			throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
					"Details 章节不存在!");
		BigInteger detailNums = getValidateBigInteger(TransXmlHelper
				.getInteger(details, ChkAccFile.XML_DETAILNUMS));
		int result = BigInteger.ZERO.compareTo(detailNums);
		if (result == 0)
			return;
		List<Element> detaillist = details.getChildren(ChkAccFile.XML_DETAIL);
		if (detaillist.size() != detailNums.intValue())
			throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
					"Detail 数目不一致!");
		for (Element de : detaillist) {
			ChkAccFileDetail cafd = new ChkAccFileDetail();
			cafd.setReqSeqNo(TransXmlHelper.getMandatoryString(de,
					ChkAccFile.XML_REQSEQNO));
			cafd.setAckSeqNo(TransXmlHelper.getMandatoryString(de,
					ChkAccFile.XML_ACKSEQNO));
			cafd.setTranType(TransXmlHelper.getMandatoryString(de,
					ChkAccFile.XML_TRANTYPE));
			cafd.setAmount(getValidateBigInteger(TransXmlHelper.getInteger(de,
					ChkAccFile.XML_AMOUNT)));
			if (!ChkAccFileDetail.checkTranType(cafd))
				throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
						"无效的交易类型!");
			fileDetail.add(cafd);
		}
		caf.setFileDetail(fileDetail);
	}

	@Override
	public ChkAccFile getChkAccFileFromRaw(byte[] content, String merchantNo,
			Date tranDate) throws PCPSApplicationException {
		SAXBuilder builder = new SAXBuilder();
		String reqXml = new String(content, Charset
				.forName(ChkAccFile.XML_CHARSET)).trim();
		Reader in = new StringReader(reqXml);
		Document doc = null;
		Element root = null;
		ChkAccFile caf = new ChkAccFile();

		try {
			doc = builder.build(in);
			root = doc.getRootElement();
			if (!PCPSUtil.compareObject(root.getName(),
					ChkAccFile.XML_CHECKFILE))
				throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
						"invalid XML Root Node Name[" + root.getName() + "]!");

			// merchantNo
			caf.setMerchantNo(TransXmlHelper.getMandatoryString(root,
					ChkAccFile.XML_MERCHANTNO));
			if (!PCPSUtil.compareObject(merchantNo, caf.getMerchantNo()))
				throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
						"商户号不一致!");

			// tranDate
			String sTranDate = TransXmlHelper.getMandatoryString(root,
					ChkAccFile.XML_TRANDATE);
			Date tranDateFile = PCPSUtil.parsePackedYMDDateTime(sTranDate);
			if (tranDateFile == null || !tranDateFile.equals(tranDate))
				throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
						"日期不一致!");
			caf.setTranDate(tranDateFile);

			// 解析summary章节
			parseSummary(root, caf);

			// 解析明细部分
			parseDetails(root, caf);

		} catch (PCPSApplicationException pa) {
			throw pa;
		} catch (Exception e) {
			PCPSUtil.Log("解析文件错!", e);
			throw new PCPSApplicationException(TuxedoRespCode.RC_PARAMETER,
					"文件内容错!");
		}

		// 设置文件内容部分
		caf.setFileContent(content);

		return caf;
	}
}